package qj.tool.gae.db;

import java.io.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import qj.tool.gae.DB;
import qj.util.IOUtil;
import qj.util.StringUtil;
import qj.util.funct.P0;
import qj.util.funct.P1;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions.Builder;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;

public class BigData {
	public Long id;
	
	public String key;
	public int part;
	
	@Unindexed
	public byte[] content;
	
	public BigData() {
	}
	
	public BigData(String key, int part, byte[] data) {
		this.key = key;
		this.part = part;
		this.content = data;
	}
	
	public static String save(InputStream in) {
		int maxLength = 1000 * 1024;
		
		String key = StringUtil.randomString(12);

		long totalLength = 0;
		for (int i = 0; ; i++) {
			byte[] buffer = IOUtil.readEnough(maxLength, in);
			totalLength+= buffer.length;
			if (buffer.length == 0) {
				break;
			}
			
			DB.save(new BigData(key, i, buffer));
			
			if (buffer.length < maxLength) {
				break;
			}
		}
		
		BigDataInfo bigDataInfo = new BigDataInfo();
		bigDataInfo.id = key;
		bigDataInfo.size = totalLength;
		DB.save(bigDataInfo);
		
		return key;
	}
	
	public static void delete(String key) {
		Query q = new Query("BigData");
		q.addFilter("key", FilterOperator.EQUAL, key);
		DB.deleteAll(q);
		DB.delete(key, DB.entityType(BigDataInfo.class));
	}

	public static byte[] load(String key) {
		final ByteArrayOutputStream bo = new ByteArrayOutputStream();
		
		eachBlock(key, new P1<byte[]>() {public void e(byte[] bytes) {
			try {
				bo.write(bytes);
			} catch (IOException e1) {
				throw new RuntimeException(e1);
			}
		}});
		return bo.toByteArray();
	}

	public static void serve(HttpServletResponse resp, String key) throws IOException {
		String contentType = "image/png";
		serve(resp, key, contentType);
	}

	private static void serve(HttpServletResponse resp, String key,
			String contentType) throws IOException {
		resp.setContentType(contentType);
		final ServletOutputStream out = resp.getOutputStream();
		
		eachBlock(key, new P1<byte[]>() {public void e(byte[] bytes) {
			try {
				out.write(bytes);
			} catch (IOException e1) {
				throw new RuntimeException(e1);
			}
		}});
		out.flush();
	}
	public static InputStream asInputStream(String key) throws IOException {
		final ByteArrayOutputStream out = new ByteArrayOutputStream();
		
		eachBlock(key, new P1<byte[]>() {public void e(byte[] bytes) {
			try {
				out.write(bytes);
			} catch (IOException e1) {
				throw new RuntimeException(e1);
			}
		}});
		
		return new ByteArrayInputStream(out.toByteArray());
	}

	private static void eachBlock(String key, P1<byte[]> eachBlockF) {
		Query query = new Query("BigData");
		query.addFilter("key", FilterOperator.EQUAL, key);
		query.addSort("part");
		for (Entity entity : DB.ds.prepare(query).asIterable(Builder.withChunkSize(1))) {
			Blob content = (Blob) entity.getProperty("content");
			eachBlockF.e(content.getBytes());
		}
	}

	public static P0 rollbackF(final String bigDataKey) {
		return new P0() {public void e() {
			try {
				delete(bigDataKey);
			} catch (Exception e1) {
				// Just rollback
			}
		}};
	}
	
	public static class BigDataInfo {
		public String id;
		
		@Unindexed
		public Long size;
		
		@Unindexed
		public String name;
		
		@Unindexed
		public String type;
		
	}
}
